/**
 ******************************************************************************
 * FileName     ： speech_sys.c
 * Date         ： 2023年5月30日
 * Author       ： wlingL
 * Brief        ：
 * Function List：
 ******************************************************************************
 *@Note
 *
 ******************************************************************************
 */

/* Includes */
#include"time.h"
/* Global Variable */

/* Functions Define */
/********************************** (C) COPYRIGHT *******************************
 * File Name          : main.c
 * Author             : WCH
 * Version            : V1.0.0
 * Date               : 2021/09/09
 * Description        : Main program body.
 *******************************************************************************/

#include "Driver/inc/speech_sys.h"
#include "Driver/inc/key.h"
#include "images.h"
#include "main.h"
/**********************************
    注意rt-thread无法打印浮点，可打开
    编译器的nano_printf选项，代码会大13KB+
**********************************/

typedef struct
{
    rt_uint8_t result;
    rt_uint8_t dir;
} result_t;

/* Global Variable */
//const char *key_words[] = {"开灯", "关灯", "开门", "关门","打开窗户", "关闭窗户" ,"打开风扇", "关闭风扇" };
const char *key_words[] = {"开灯", "关灯", "开门", "关门","打开风扇", "关闭风扇","离家模式" };
rt_uint16_t V_Data[VDBUF_LEN * 2] = {1}; // 放到data段,放bss不行，待确认
rt_atap_arg atap_arg;
rt_active_voice active_voice[max_vc_con];

__attribute__((aligned(4))) rt_ftv_arg ftv_arg;
struct rt_mailbox voice_rec_mb;
static rt_uint32_t voice_rcg_mb_pool[4];
struct rt_mailbox display_mb;
static rt_uint32_t display_mb_pool[4];
static rt_mutex_t V_Data_mutex = RT_NULL;
result_t rcg_result;

ALIGN(RT_ALIGN_SIZE)
static char recongnition_stack[2048];
static struct rt_thread recongnition_thread;

ALIGN(RT_ALIGN_SIZE)
static char display_stack[2048];
static struct rt_thread display_thread;

 rt_uint8_t flag_speaking = 0;
unsigned char inst0_do = 0; // 开灯控制
unsigned char inst1_do = 0; // 关灯控制
unsigned char inst2_do = 0; //开门控制
unsigned char inst3_do = 0; //关门控制
unsigned char inst4_do = 0; //开风扇
unsigned char inst5_do = 0; //

unsigned char inst6_do = 0; //离家模式
unsigned char inst7_do = 0;//关
unsigned char inst8_do = 0;//自动控制
unsigned char inst9_do = 0;


unsigned int  state_door = 0;//门的状态
unsigned int  state_lamp = 0;//灯的状态
unsigned int  state_curtain = 0;//窗帘的状态

volatile float  c,t;//实际湿度与温度
volatile float  state_c = 100;//预计湿度
volatile float  state_t = 24;//预计温度
int start_time = 0;
/* 触发采集 */
void voice_record(void)
{
#ifdef BSP_USING_ES8388

    I2S_Cmd(SPI2, ENABLE);
    rt_thread_mdelay(start_delay);
    DMA_Rx_Init(DMA1_Channel4, (u32)&SPI2->DATAR, (u32)V_Data, (VDBUF_LEN * 2));
    DMA_Cmd(DMA1_Channel4, ENABLE);

    rt_thread_mdelay(Noise_len_t); // 空菜时间段
    // lcd_fill(81, 0, 239, 60, WHITE);
    // lcd_set_color(BLACK, BLUE);

    lcd_x = 240 - 11 * 12;
    lcd_y = 0;
    lcd_fill(lcd_x, lcd_y, lcd_x + 12 * 11, lcd_y + 24 * 1,BLACK);
    lcd_show_string(lcd_x, lcd_y, 24, "speaking...");
    // lcd_show_string(256 - 11 * 12, 0, 24, "stop.");

#else
    rt_thread_mdelay(20);
    TIM_Cmd(TIM1, ENABLE);
    rt_thread_mdelay(Noise_len_t);

    lcd_fill(81, 0, 239, 60, WHITE);
    lcd_set_color(WHITE, BLUE);
    lcd_show_string(90, 7, 24, "speaking...");
#endif
}

/* mel模板保存 */
rt_uint8_t save_mdl(rt_uint16_t *v_dat, rt_uint32_t addr)
{
    environment_noise(v_dat, Noise_len, &atap_arg);
    active_detect(v_dat, VDBUF_LEN, active_voice, &atap_arg);
    if (active_voice[0].end == ((void *)0))
    {
        return VAD_fail;
    }
    cal_mfcc_ftv(&(active_voice[0]), &ftv_arg, &atap_arg);
    if (ftv_arg.frm_num == 0)
    {
        return MFCC_fail;
    }
    return save_ftv_mdl(&ftv_arg, addr);
}

void ch9141_send(rt_uint8_t *data, rt_uint16_t len);

/* 语音识别线程入口函数  */
void voice_recongnition_entry(void *parameter)
{
    static u8 rcg_count = 0;
    rt_uint16_t i = 0, min_comm = 0;
    rt_uint32_t match_dis, ftvmd_addr, cur_dis, min_dis = 0;
    rt_ftv_arg *ftv_mdl;
    rt_uint32_t buffer = 0; // 值为V_Data的地址，可用可不用

    while (1)
    {

        if (rt_pin_read(wake_up) == PIN_HIGH)
        {                        // 判断是否是上按键按下
            rt_thread_mdelay(5); // 去抖动
            if (rt_pin_read(wake_up) == PIN_HIGH)
            { // 判断是否是唤醒wakeup按键保持按下或者远程发出指令
                flag_speaking = 1;

                // led_Toggle(LED2_PIN);
            }
        }
        if (flag_speaking == 1)
        {

            rt_mutex_take(V_Data_mutex, RT_WAITING_FOREVER);
            flag_speaking = 0;
            /* 采集触发 */
            rt_kprintf("please speaking:\n");
            
            voice_record();
        //    int a = CLOCKS_PER_SEC;

            // inst0_do = 1;
            
            if (rt_mb_recv(&voice_rec_mb, (rt_ubase_t *)&buffer, RT_WAITING_FOREVER) == RT_EOK) // 传递V_Data，全局变量随便用哪个
            {
                // lcd_set_color(WHITE, RED);
                // lcd_fill(81, 0, 239, 60, WHITE);
                lcd_x = 240 - 11 * 12;
                lcd_y = 0;
                lcd_fill(lcd_x, lcd_y, lcd_x + 12 * 11, lcd_y + 24 * 1, BLACK);
                lcd_show_string(lcd_x, lcd_y, 24, "stop...");
                rt_kprintf("begin rcg:\n");
                start_time = clock();
                environment_noise(V_Data, Noise_len, &atap_arg);
                active_detect(V_Data, VDBUF_LEN, active_voice, &atap_arg);
                if (active_voice[0].end == ((void *)0))
                {
                    match_dis = MATCH_ERR;
                }
                cal_mfcc_ftv(&(active_voice[0]), &ftv_arg, &atap_arg);
                if (ftv_arg.frm_num == 0)
                {
                    match_dis = MATCH_ERR;
                }

                i = 0;
                min_dis = MATCH_MAX;
                for (ftvmd_addr = ftvmd_start_addr; ftvmd_addr < ftvmd_end_addr; ftvmd_addr += size_per_ftv)
                {
                    /* 模板按rt_ftv_arg存储，直接获取首地址 */
                    ftv_mdl = (rt_ftv_arg *)ftvmd_addr;
                    cur_dis = ((ftv_mdl->save_sign) == SAVE_FLAG) ? cal_match_dis(&ftv_arg, ftv_mdl) : MATCH_ERR;
                    if (cur_dis < min_dis)
                    {
                        min_dis = cur_dis;
                        min_comm = i;
                    }
                    i++;
                }
                min_comm = min_comm / ftvmd_per_word;
                if (min_dis >= Valid_Thl)
                    match_dis = MATCH_ERR; // 较大时认为不可信，识别失败

                if (match_dis == MATCH_ERR)
                {
                    match_dis = 0;
                    rcg_result.dir = 0xFF;
                    rcg_result.result = RCG_ERR;
                }
                else
                {
                    rcg_result.dir = min_comm;
                    rcg_result.result = RCG_EOK;
                    //                rt_kprintf("recg end,your speaking is: %s\r\n",key_words[min_comm]);
                }

                rt_mb_send(&display_mb, (rt_ubase_t)&rcg_result); // 结果发生给显示
                rcg_count++;
                rt_kprintf("rcg success counts:%d\n", rcg_count);
            }
            rt_mutex_release(V_Data_mutex);
        }
        // rt_kprintf("识别线程循环%d\n", rcg_count);

        rt_thread_mdelay(10);
    }
}

void lcd_display(void *parameter)
{
    static uint8_t print_value[12];

    static rt_uint8_t i = 1;
    rt_int16_t x1 = 120, y1 = 150, x2, y2; // 红色小块初始坐标
    rt_uint32_t result = 0;
    rt_uint8_t bledata[4] = {0};
    x2 = x1 + 10;
    y2 = y1 + 10;

    while (1)
    {
        if (rt_mb_recv(&display_mb, (rt_ubase_t *)&result, RT_WAITING_FOREVER) == RT_EOK)
        {
            if (rcg_result.result == RCG_ERR)
            {
                // lcd_show_string(255, 255, 24, "0");
                // lcd_show_string(255-24, 255-24, 24, "1");
            }
            else
            {
                switch (rcg_result.dir)
                {
                case inst0:
                    lcd_show_string(0, 0, 24, "inst0");
                    inst0_do = 1;
                    rt_kprintf("speechctrl:inst0 do:%d\n", inst0_do);
                   
                    break;
                case inst1:
                    lcd_show_string(0, 0, 24, "inst1");
                    inst1_do = 1;
                    rt_kprintf("speechctrl:inst1 do:%d\n", inst1_do);
                    break;
                case inst2:
                    lcd_show_string(0, 0, 24, "inst2");
                    inst2_do = 1;
                    rt_kprintf("speechctrl:inst2 do:%d\n", inst2_do);
                    break;
                case inst3:
                    lcd_show_string(0, 0, 24, "inst3");
                    inst3_do = 1;
                    rt_kprintf("speechctrl:inst3 do:%d\n", inst3_do);
                    break;
                case inst4:
                    lcd_show_string(0, 0, 24, "inst4");
                    inst4_do = 1;
                    rt_kprintf("speechctrl:inst4 do:%d\n", inst4_do);
                    break;
                case inst5:
                    lcd_show_string(0, 0, 24, "inst5");
                    inst5_do = 1;
                    rt_kprintf("speechctrl:inst5 do:%d\n", inst5_do);
                    break;
                case inst6:
                    lcd_show_string(0, 0, 24, "inst6");
                    inst6_do = 1;
                    rt_kprintf("speechctrl:inst6 do:%d\n", inst6_do);
                    break;
//                case inst7:
//                    lcd_show_string(0, 0, 24, "inst7");
//                    inst7_do = 1;
//                    rt_kprintf("speechctrl:inst0 do:%d\n", inst7_do);
//                    break;
//                case inst8:
//                    lcd_show_string(0, 0, 24, "inst8");
//                    inst8_do = 1;
//                    rt_kprintf("speechctrl:inst0 do:%d\n", inst8_do);
//                    break;
//                case inst9:
//                    lcd_show_string(0, 0, 24, "inst9");
//                    inst9_do = 1;
//                    rt_kprintf("speechctrl:inst0 do:%d\n", inst9_do);
//                    break;
                default:
                    lcd_show_string(0, 0, 24, "no inst");
                    rt_kprintf("speechctrl:no inst \n");
                    break;
                }
            }
        }

        // rt_thread_mdelay(50); // 延时10毫秒
    }
}

int speech_sys(void)
{
    rt_err_t result;
    rt_uint8_t flash_sta;

    /* 训练和正常采集不能同时进行  */
    V_Data_mutex = rt_mutex_create("vcbmtx", RT_IPC_FLAG_PRIO);
    if (V_Data_mutex == RT_NULL)
    {
        rt_kprintf("create dynamic mutex failed.\n");
        return -1;
    }
    /* 采集完成通知识别线程 */
    result = rt_mb_init(&voice_rec_mb, "vrecmb", &voice_rcg_mb_pool[0], sizeof(voice_rcg_mb_pool) / 4, RT_IPC_FLAG_FIFO);
    if (result != RT_EOK)
    {
        rt_kprintf("init vrecmb failed.\n");
        return -1;
    }
    /* 识别完成通知显示线程 */
    result = rt_mb_init(&display_mb, "lcdmb", &display_mb_pool[0], sizeof(display_mb_pool) / 4, RT_IPC_FLAG_FIFO);
    if (result != RT_EOK)
    {
        rt_kprintf("init lcdmb failed.\n");
        return -1;
    }

    rt_thread_init(&recongnition_thread, "vrcg", voice_recongnition_entry, RT_NULL, &recongnition_stack[0],
                   sizeof(recongnition_stack), 4, 20);
    rt_thread_startup(&recongnition_thread);

    rt_thread_init(&display_thread, "display", lcd_display, RT_NULL, &display_stack[0], sizeof(display_stack), 10, 20);
    rt_thread_startup(&display_thread);
}

/* 模板训练，导入到fish命令行，根据命令行提示训练和导入模板 */
int practice(void)
{
    rt_uint32_t buffer = 0, i = 0;
    rt_uint8_t retry_cnt = 0;
    rt_uint32_t addr;

    rt_mutex_take(V_Data_mutex, RT_WAITING_FOREVER); // buffer 互斥
    while (1)
    {

        rt_kprintf("\r\npractice start...\r\n\r\n");

        for (i = 0; i < word_num * ftvmd_per_word; i++) // 4位单词个数
        {
        retry:
            printf("please speak:%s \r\n", key_words[i / ftvmd_per_word]);
            voice_record();
            addr = ftvmd_start_addr + (i / ftvmd_per_word) * size_per_word + (i % ftvmd_per_word) * size_per_ftv;
            rt_kprintf("addr:%08x\r\n", addr);

            if (rt_mb_recv(&voice_rec_mb, (rt_ubase_t *)&buffer, RT_WAITING_FOREVER) == RT_EOK) // 等待DMA传输完成
            {
                if (save_mdl(V_Data, addr) == PRG_SUCCESS)
                {
                    printf("\r\n %s practice success\r\n", key_words[i / ftvmd_per_word]);
                }
                else
                {
                    printf("%s practice fail !!!\r\n", key_words[i / ftvmd_per_word]);
                    retry_cnt++;
                    if (retry_cnt < 3)
                        goto retry;
                    else
                        break;
                }
            }
            Hal_Delay_Ms(1000); // 硬件延迟，整个循环只在需要的时候执行一遍，不需要切换
            Hal_Delay_Ms(1000);
        }
        rt_kprintf("practice end!!!\r\n");
        break;
    }

    rt_mutex_release(V_Data_mutex);
    return 0;
}
MSH_CMD_EXPORT(practice, practice voice modle and store in flash);

/* End of File */
